<?php

declare(strict_types=1);

namespace Zms\Validate\Rule;

use Zms\Unit\Is;
use Zms\Unit\Number;

/**
 * 数字验证器
 */
class NumberValidate extends RuleAbstract
{
    /**
     * 默认错误消息
     * @var string
     */
    protected $_error = '{label}必须是数字';

    /**
     * 最小值
     * @var float|int|null
     */
    protected $_min = null;

    /**
     * 最小值错误提示信息
     * @var string
     */
    protected $_minError = "{label}不能小于{min}";

    /**
     * 最大值
     * @var int|float|null
     */
    protected $_max = null;

    /**
     * 最大值错误提示信息
     * @var string
     */
    protected $_maxError = "{label}不能大于{max}";

    /**
     * 精度（小数点后位数）
     * @var int|null
     */
    protected $_precision = null;

    /**
     * 精度错误提示信息
     * @var string
     */
    protected $_precisionError = "{label}只能精确到{precision}位小数";

    /**
     * 限制数字最大值
     * @param int|float $max
     * @return $this
     */
    public function setMax($max): NumberValidate
    {
        $this->_max = $max;
        return $this;
    }

    /**
     * 限制数字最大值错误提示
     * @param string $maxError
     * @return $this
     */
    public function setMaxError(string $maxError): NumberValidate
    {
        $this->_maxError = $maxError;
        return $this;
    }

    /**
     * 限制数字最小值
     * @param int|float $min
     * @return $this
     */
    public function setMin($min): NumberValidate
    {
        $this->_min = $min;
        return $this;
    }

    /**
     * 限制数字最小值错误提示
     * @param string $minError
     * @return $this
     */
    public function setMinError(string $minError): NumberValidate
    {
        $this->_minError = $minError;
        return $this;
    }

    /**
     * 设置精度
     * @param int $precision
     * @return $this
     */
    public function setPrecision(int $precision): NumberValidate
    {
        $this->_precision = $precision;
        return $this;
    }

    /**
     * 设置精度错误提示
     * @param string $precisionError
     * @return $this
     */
    public function setPrecisionError(string $precisionError): NumberValidate
    {
        $this->_precisionError = $precisionError;
        return $this;
    }

    /**
     * 开始验证
     * @param mixed $value 需要验证的值
     * @param string $label 字段标签，用于错误提示信息中
     * @return bool 验证结果：成功为 true，失败为 false
     */
    public function validate($value, string $label = ''): bool
    {
        if (is_numeric($value)) {
            if (Is::empty($value) && $this->_noSkipping === true) {
                $this->_value = $value;
                return true;
            }
            $num = Number::format($value);
            if (is_int($this->_min) || is_float($this->_min)) {
                if ($num < $this->_min) {
                    $this->addError(str_replace("{min}", strval($this->_min), $this->_minError), $label);
                }
            }
            if (is_int($this->_max) || is_float($this->_max)) {
                if ($num > $this->_max) {
                    $this->addError(str_replace("{max}", strval($this->_max), $this->_maxError), $label);
                }
            }
            if (is_numeric($this->_precision) && Number::precision($num) > $this->_precision) {
                $this->addError(str_replace("{precision}", strval($this->_precision), $this->_precisionError), $label);
            }
            if ($this->isFail()) {
                return false;
            }
            $this->_value = $value;
            return true;
        } elseif (is_string($value) && Is::empty($value)) {
            if ($this->_noSkipping) {
                $this->_value = $value;
                return true;
            }
        }
        $this->addError($this->_error, $label);
        return false;
    }
}